import axios from 'axios'
import qs from 'query-string'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import NProgress from 'nprogress'
import { Message, Notification } from '@arco-design/web-vue'
import { useUserStore } from '@/stores'
import { TOKEN_KEY, getToken } from '@/utils/auth'

interface ICodeMessage {
  [propName: number]: string
}

const StatusCodeMessage: ICodeMessage = {
  200: '服务器成功返回请求的数据',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队（异步任务）',
  204: '删除数据成功',
  400: '请求错误(400)',
  401: '登录信息失效，请重新登录(401)',
  403: '权限不足(403)',
  404: '请求出错(404)',
  408: '请求超时(408)',
  500: '服务器错误(500)',
  501: '服务未实现(501)',
  502: '服务维护中(502)',
  503: '服务不可用(503)',
  504: '网络超时(504)',
}

const http: AxiosInstance = axios.create({
  // baseURL: import.meta.env.VITE_API_BASE_URL,
  baseURL: '/api',
  timeout: 30 * 1000,
  paramsSerializer: (params) => {
    return qs.stringify(params)
  },
})

// 请求拦截器
http.interceptors.request.use((config) => {
  NProgress.start() // 进度条
  // 添加token到header
  config.headers[TOKEN_KEY] = getToken()
  return config
}, (error) => {
  return Promise.reject(error)
})

// 响应拦截器
http.interceptors.response.use((response: AxiosResponse) => {
  NProgress.done()
  if (response.request.responseType === 'blob') {
    return response
  }
  return response.data
}, (error) => {
  NProgress.done()
  const response = Object.assign({}, error.response)
  const status = Number(response.status) || 500
  const message = error.response.data?.message || StatusCodeMessage[response.status] || '系统异常, 请检查网络或联系管理员！'
  // 如果错误信息长度过长，使用 Notification 进行提示
  if (message.length <= 15) {
    Message.error(message)
  } else {
    Notification.error(message)
  }
  if (status === 401) {
    const userStore = useUserStore()
    userStore.logout()
    return Promise.reject(new Error('登录信息失效，请重新登录'))
  }
  return Promise.reject(error)
})

const request = <T = unknown>(config: AxiosRequestConfig): Promise<T> => {
  return new Promise((resolve, reject) => {
    http
      .request<T>(config)
      .then((res: AxiosResponse) => resolve(res.data))
      .catch((err: { msg: string }) => reject(err))
  })
}

const requestNative = <T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse> => {
  return new Promise((resolve, reject) => {
    http
      .request<T>(config)
      .then((res: AxiosResponse) => resolve(res))
      .catch((err: { msg: string }) => reject(err))
  })
}

const get = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<T> => {
  return request({
    method: 'get',
    url,
    params,
    ...config,
  })
}

const post = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<T> => {
  return request({
    method: 'post',
    url,
    data: params,
    ...config,
  })
}

const put = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<T> => {
  return request({
    method: 'put',
    url,
    data: params,
    ...config,
  })
}

const patch = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<T> => {
  return request({
    method: 'patch',
    url,
    data: params,
    ...config,
  })
}

const del = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<T> => {
  return request({
    method: 'delete',
    url,
    data: params,
    ...config,
  })
}
const download = (url: string, params?: object, config?: AxiosRequestConfig): Promise<AxiosResponse> => {
  return requestNative({
    method: 'get',
    url,
    responseType: 'blob',
    params,
    paramsSerializer: (obj) => {
      return qs.stringify(obj)
    },
    ...config,
  })
}
export default { get, post, put, patch, del, request, requestNative, download }
